----------------------------------------------------------------------
--- Common extensions to the base functionality

if not __DO_ONCE__ then
  __DO_ONCE__ = true
  --Modify module paths
  package.path = package.path..";.\\?.lua;.\\?\\?.lua;..\\?.lua;..\\?\\?.lua"
  package.cpath = package.cpath..";.\\?.dll;.\\?\\?.dll;..\\?.dll;..\\?\\?.dll"
end

if __DEV__ then
  --override "require" with a version that always reloads modules
  --this gives us fast iteration for modules, and improves development speed
  truerequire = truerequire or require
  local dontreload = {
    coroutine = true,
    debug  = true,
    io     = true,
    math   = true,
    os     = true,
    package = true,
    string = true,
    table  = true,
  }   
  function require(name)
    if dontreload[name] ~= true then 
      package.loaded[name]=nil
      local m = truerequire(name)
      if m then
        if (type(m) ~= "boolean") then
           package.loaded[name].__type="module"
        end
        print("loaded module",name)
      else
         print("failed to load module",name)
      end
      return package.loaded[name]
    else
      return package.loaded[name]
    end
  end
else  
  truerequire = truerequire or require  
end

--- boolean aliases to aid code readability
off = false
on  = true
yes = true
no  = false

--- Bring commonly used module functions to the global namespace
rand   = math.random
format = string.format
string.tolower  = string.lower
string.toupper  = string.upper
tolower  = string.lower
toupper  = string.upper

exit     = os.exit
--- Print formated string
function printf(s,...)
  print(format(s,...))
end

--- A function that does nothing
nullfunction = function(...) return nil end

--- a none object
none = function()
 return none
end

null = none --- Null is None

if __DEBUG__ then
  debug.assert = assert
  debug.print  = print
  debug.printf = printf  
else
  --Ignore asserts
  dummy = nullfunction
  debug.assert = dummy
  debug.print  = dummy
  debug.printf = dummy  
end

printd = debug.print

log = function (when, where, who, what, why) printf("log[%s:%s:%s:%s:%s]",when,where,who,what,why) end

local deprecate_mt = {
  __index = function(t,k)
    printd(t.__deprecate_msg)
    return t.__deprecate_table[k]
  end,
  __newindex = function(t,k,v)
    printd(t.__deprecate_msg)
    t.__deprecate_table[k] = v
  end,
}

local printd = debug.print or print
 
function deprecate(o,msg)
  local msg = msg or "deprecated call"
  if type(o) == "function" then
    return function(...)
            printf("deprecated[%s]",msg) 
            return o(...)
           end
  else
    local dmt = getmetatable(o)
    dmt.__index = function(t,k)
      printd(t.__deprecate_msg)
      return t.__deprecate_table[k]
    end
    dmt.__newindex = function(t,k,v)
      printf("deprecated[%s]",t.__deprecate_msg)
      t.__deprecate_table[k] = v
    end
    dt = {
      __deprecate_msg = msg,
      __deprecate_table = o,
    }
    setmetatable(dt,dmt)
    return dt
  end
end

--- Extended type definitions
local truetype = truetype or type -->without this check when quick iteration 
                            -- is on this might cause the new type function 
                            -- to be referencing itself causing an infinite 
                            -- loop 
function type(v)
  local v = v
  local s = truetype(v)
  if s == "table" then
    if v.__type then
      s = v.__type
    end 
  end
  return s
end 

--- Extended type definitions
local truetostring = truetostring or tostring -->without this check when quick iteration 
                            -- is on this might cause the new type function 
                            -- to be referencing itself causing an infinite 
                            -- loop
function tostring(v)
  if type(v)=="table" then
    local mt = getmetatable(v)
    if mt and (mt.__tostring == nil) then
      tabletostring(v)
    end
  end
  if type(v)=="module" then
    return string.format("module[%s]",v._NAME or "unknown")
  end  
  return truetostring(v)
end 

                            
function tabletostring(t)
    local s ="{"
    for k,v in pairs(t) do
      s = string.format("%s%s=%s;",s,k,tostring(t))
    end
    return s.."}"
end
         
--debug = debug or {}
debug.func = {
  name = {
    this = function()
      local t = debug.getinfo(2)
      return format("%s[%s]",t.name,t.namewhat)
    end,
    prev = function()
      local t = debug.getinfo(3)
      return format("%s[%s]",t.name,t.namewhat)
    end,
  }, 
  location = {
    this = function()
      local t = debug.getinfo(2)
      return format("%s[line:%s]",t.source,t.linedefined)
    end,
    prev = function()
      local t = debug.getinfo(3)
      return format("%s[line:%s]",t.source,t.linedefined)
    end,
  },
  call = {
    this = function()
      local t = debug.getinfo(2,'f')
      return t.func
    end,
    prev = function()
      local t = debug.getinfo(3,'f')
      return t.func
    end,   
   },
   trace = function()
     return debug.traceback()
   end,
}

--- Make the program wait
function wait(seconds)
  local socket = socket or require"socket"
  if socket then
    socket.sleep(seconds)
  else -- We can't find the socket module so we busy sleep instead 
    debug.print("Couldn't find the socket module. Doing a busy wait instead")
    local t = os.time() + seconds
    while (os.time() < t) do
    end
  end
end

sleep = wait

--- Clone tables, as opposed to referencing them
clone = function(t)
  local u = setmetatable ({}, getmetatable (t))
  local k,v
  for k, v in pairs (t) do
    if truetype(v)=="table" then
      if v ~= t then -->avoid cycles
        u[k] = clone(v)-->recursively clone subtables
      end
    else
      u[k] = v
    end
  end
  return u
end


--- Wrapup a function to use memoization
if __OPTIMIZE__ then
  function memoize (fun)
    local __results ={}
    setmetatable(__results, {__mode = "kv"})  
    return function(s)
      if __results[s]~=nil then
        return __results[s]
      else
        local v = fun(s)
        __results[s] = v
        return v 
      end
    end
  end
else
  function memoize (fun)
    return fun
  end
end      

--- Extended require, that brings everything from a module or table into the global namespace
function using(t)
  local t = t
  if type(t) == "string" then
    t = require(t)
  end
  local k,v 
  for k,v in pairs(t) do
    if _G[k] ~= nil then 
      printf("redefinition of global %s",k)
    end 
	_G[k] = _G[k] or v  -- don't overwrite existing variables
  end
end

--> Weak table
local weaktable_mt = {__mode="k"}

function weaktable(t)
  local t = t or {}
  mt = getmetatable(t) or {}
  mt.__mode =  weaktable_mt.__mode
  setmetatable(t,mt)
  return t
end

--> Readonly table
local readonly_mt = {
  __index = function(t,k)
    return t.__readonly[k]
  end,  
  __newindex = function (t,k,v)
    error("attempt to update a read-only table", 2)
  end
}

function readonly (t)
  local ro ={}
  ro.__readonly = t
  setmetatable(ro, readonly_mt)
  return ro
end

final_mt = {
  __newindex = function (t,k,v)
    error("attempt to update a read-only table", 2)
  end
}

function final (t)
  setmetatable(t, final_mt)
  return t
end

function has(list,v)  -->Check if an array has a value
  local i,t
  local list = list
  for i,t in pairs(list) do
    if t == v then 
      return t 
    end
  end
  return nil
end

function compare(s1,s2)
  if s2 == "" then
    return false
  end
  local s2 =s2
  if s2 == "*" then
    s2 = ".+" -->Match anything,except empty strings
  end
  if (string.find(tolower(s1),tolower(s2)) ~= nil) or (string.find(tolower(s2),tolower(s1)) ~= nil) then
    return true
  else
    return false
  end    
end

function string.tokenize(s)
 local words = {}
 string.gsub(s, "([%w]+)", function (w)
   table.insert(words, w)
 end)
 return words
end

function string.explode(s)
 local words = {}
 string.gsub(s, "([%p%w]+)", function (w)
   table.insert(words, w)
 end)
 return words
end

function string.trim (s)
  return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
end

function string.expand (s)
  s = string.gsub(s, "$(%w+)", function (n)
        return tostring(tokens[n])
      end)
  return s
end

string.hex = {}
string.hex.encode = function(s)
  local hex = ""
  for b in string.gfind(s, ".") do
    hex = hex + (string.format("%02X ", string.byte(b)))
  end
  return hex
end

string.fillout = function(tokens,instring)
   local outstring = instring
   local k,v
   for k,v in pairs(tokens) do
       outstring = string.gsub(outstring, "$"..k, tostring(v))
   end
   return outstring
end

require "mime"
string.base64 = {}
string.base64.encode = mime.encode("base64")
string.base64.decode = mime.decode("base64")

 
function r(s)
  return string.format("%q",s)
end

function filter(fun, t)
  local r = {}
  for k,v in ipairs(t) do 
    if fun(t[k]) then
      table.insert(r,v)
    end
  end
  return r
end
--  t = filter(function(x) if x=1 then return false else return true end end,{1,2,3}) --> map(print,t) --> 2,3
function map(fun,t)
  for k,v in ipairs(t) do
    t[k] = fun(v)
  end
  return t
end

--t = map(function(x) return x*x*x end, {1,2,3})  --> map(print,t) --> {1,8,27}
function reduce(fun,t)
  local r,i=nil,1
  r = r or t[1]
  while #t > i do
    i = i +1
    r = fun(r,t[i])
  end
  return r
end
--reduce(function(x,y) return x+y end,{1,2,3,4,5,6,7,8,9,10}) --> 55

function symbol(s)
  local new = s
  local old = _G[s]
 _G[s] = old or new
 if (old ~= nil) and (old ~= new) then
   error(string.format("symbol %q has already been defined by %q",tostring(new),tostring(old)))
 end
 return new
end

s = symbol
--s"LALA" --> print(LALA)=LALA

createtoken = deprecate(function ()
  return "temp"..math.floor((rand()*1000000000))
end, 
"using the flakey createtoken")

encrypt = deprecate(function (s)
  local s = s or ""
  return "~encrypted~"..s
end,
"using the flakey encrypt")


function serialize (o)
  if type(o) == "number" then
    return tostring(o)
  else   -- assume it is a string
    return string.format("%q", o)
  end
end

local function _marshall (name, value, separator, saved)
  local separator = separator or "\n"
  local saved = saved or {}       -- initial value
  local data = {}
  table.insert(data,name.." = ")
  if type(value) == "number" or type(value) == "string" then
    table.insert(data,serialize(value)..separator)
  elseif type(value) == "table" then
    if saved[value] then    -- value already saved?
      table.insert(data,saved[value]..separator)  -- use its previous name
    else
      saved[value] = name   -- save name for next time
      table.insert(data,"{}"..separator)     -- create a new table
      for k,v in pairs(value) do      -- save its fields
        local fieldname = string.format("%s[%s]", name,
                                        serialize(k))
        table.insert(data,_marshall(fieldname, v, separator, saved))
      end
    end
  else
    error("cannot save [" .. name.. "] a ".. type(value))
  end
  return table.concat(data,"")
end

function marshall(name, value, separator, saved)
  local separator = separator or "\n" 
  local data = {}
  table.insert(data,string.format("do local ",separator))
  table.insert(data,_marshall(name,value,separator,saved))
  table.insert(data,string.format("return %s%send",name,separator))
  return table.concat(data,"")
end

function unmarshall(s)
  t = {}
  local fun,err = loadstring(s)
  if fun ~= nil then
    t = fun()
  else
    t = s
  end
  return t
end

file = {}
file.save = function (filename,data)
  local f = assert(io.open(filename,"w"))
  f:write(data)
  assert(f:close())
end

file.load = function (filename)
  local f = assert(io.open(filename,"r"))
  local data = f:read("*all")
  assert(f:close())
  return data
end

file.append = function (filename,data)
  local f = assert(io.open(filename,"a"))
  f:write(data)
  assert(f:close())
end

help = function(module)
  print("*************** help ****************")
  print("Module:",module._NAME)
  print("-------------------------------------")
  if module.__help then
     print(module.__help)
  end
  for k,v in pairs(module) do
    if string.find(k,"^_")==nil then
      print(k,v)  
    end
  end
  print("*************************************")
end
